﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using Devart.Data.Linq;
using Ext.Net;
using KamLib.Converts;

namespace KamLib
{

    public abstract class EntitiesFormLogic<TFormControl, TFormData, TEntities>
        : FormLogic<TFormControl, TFormData>, IEntitiesContainer
        where TFormControl : WindowFormLogicControl
        where TFormData : class, new()
        where TEntities : DataContext, new()
    {
        public TEntities db;
        DataContext IEntitiesContainer.Entities { get { return db; } set { db = (TEntities)value; } }

        public override void Assign(UiLogicNode src)
        {
            base.Assign(src);

            var sEntities = src as IEntitiesContainer;
            if (sEntities == null) return;
            var entities = sEntities.Entities as TEntities;
            if (entities != null) db = entities;
        }

        public override void Activate()
        {
            if (db == null)
                db = new TEntities();
        }

        public override void Dispose()
        {
            if (db == null) return;
            db.Dispose();
            db = null;
        }
    }


    public abstract class EntityFormLogic<TFormControl, TFormData, TEntities, TEntity>
        : EntitiesFormLogic<TFormControl, TFormData, TEntities>
        where TFormControl : WindowFormLogicControl
        where TFormData : class, new()
        where TEntities : DataContext, new()
        where TEntity : class, new()
    {
        public TEntity Entity;

        /// <summary>
        /// Выражение вида:
        /// a => new FormData { Field1 = a.Parent.Field1, ... }
        /// Используется в Select для полей, которые берутся из других Entities
        /// </summary>
        protected static Expression<Func<TEntity, TFormData>> SelectInit;

        static readonly Regex re1 = new Regex(@"\((\d+)\)", RegexOptions.Compiled);

        protected override List<UiFormFieldInfo> CreateFieldInfos()
        {
            Activate();

            var infos = base.CreateFieldInfos();

            var etype = typeof(TEntity);
            var mt = db.Mapping.GetTable(etype);

            foreach (var info in infos)
            {
                var emember = etype.GetPropertyOrField(info.Name);
                if (emember == null) continue;
                info.Nullable = emember.ResultType().IsNullable();

                var mm = mt.RowType.GetDataMember(emember);
                if (mm == null) continue;

                info.Nullable &= mm.CanBeNull;

                if (mm.DbType == null) continue;
                var m = re1.Match(mm.DbType);
                if (m.Success)
                    info.MaxLength = m.Groups[1].Value.ToInt();
            }

            return infos;
        }


        protected override TFormData Select()
        {
            var values = Select(SelectInit);
            SelectLocalDetails(values);
            return values;
        }

        protected static Expression<Func<TEntity, TFormData>> selectExpression;
        protected TFormData Select(Expression<Func<TEntity, TFormData>> init)
        {
            var query = Table().Select(ref selectExpression, init).Filter(Key.ID);
            return query.FirstOrDefault();
        }

        protected override void SaveEntities()
        {
            var tbl = Table();

            Entity = Key.IsNew ? new TEntity() : tbl.Filter(Key.ID).FirstOrDefault();

            if (Entity == null)
                throw new Exception("The " + EntityDesc.Name + " with id = " + Key + " is not exist.");

            if (Key.MasterID != null) Key.MasterID.CopyTo(Values);
            PreSaveValues();
            AfterPreSaveValues();
            if (Key.MasterID != null) Entity.CopyFrom(Key.MasterID);
            Entity.CopyFrom(Values);

            if (Key.IsNew)
            {
                Table().InsertOnSubmit(Entity);
            }

            SaveLocalChanges();
            db.SubmitChanges();

            foreach (var keyfield in KeyFields)
            {
                try
                {
                    Key[keyfield] = typeof(TEntity).GetValue(Entity, keyfield, null);
                }
                catch (Exception ex)
                {
                    throw new Exception("Can't get KeyField '" + keyfield + "' from entity '" + typeof(TEntity).Name + "'. \r\n" + ex.FullMessage());
                }
            }
            Values = null;
        }

        public override void DeleteEntity()
        {
            var tbl = Table();
            var entity = tbl.Filter(Key.ID).FirstOrDefault();
            if (entity == null) return;
            tbl.DeleteOnSubmit(entity);
            db.SubmitChanges();

            ReloadDependentStores();
        }


        public Table<TEntity> Table()
        {
            return db.GetTable<TEntity>();
        }

    }


    public abstract class EntityFormLogic<TFormData, TEntities, TEntity>
        : EntityFormLogic<WindowFormLogicControl, TFormData, TEntities, TEntity>
        where TFormData : class, new()
        where TEntities : DataContext, new()
        where TEntity : class, new()
    {
    }

}